//
//  tdl/sys/soloader.h
//  Created by tommywang on 13/04/27.
//

#ifndef tdl_sys_soloader_h
#define tdl_sys_soloader_h

extern "C" {
#include <errno.h>
#include <dlfcn.h>
}
#include <string>
using namespace std;
#include "tdlexcept.h"
#include "meta/btypes.h"
#include "meta/tlc_gen.h"
using namespace tdl;

namespace tdl {
////////////////////
template <typename __func_ptr_typelist>
class soloader
{
protected:
	template <typename Tpfun>
	struct sofunc_info
	{
		uint32_t		func_id;
		Tpfun			func_ptr;
	public:
		sofunc_info() : func_id(-1), func_ptr(NULL) {}
		void set(uint32_t id, void* pf)
		{
			func_id = id;
			func_ptr = (Tpfun)pf;
		}
	};

	typedef __func_ptr_typelist		fp_typelist;

protected:
	bool			m_enable;
	void*			m_soh;
	std::string		m_so_path;
	tlc::gen_scatter<fp_typelist, sofunc_info>	m_pfuncs;

public:
	soloader() : m_enable(false), m_soh(NULL), m_so_path("") {}

	~soloader()
	{
		release();
	}

	void initialize(const std::string& sopath) throw(soload_ex)
	{
		m_soh = ::dlopen(sopath.c_str(), RTLD_LAZY);
		if(m_soh == NULL)
			THROW2(soload_ex, TRACE_INFO() << "dlopen('" << sopath << "')");
			
		m_so_path = sopath;
		m_enable = true;
	}

	template <uint32_t FID>
	void load_sofunc(const std::string& func_name) throw(soload_ex)
	{
		void* sopf = ::dlsym(m_soh, func_name.c_str());
		if(sopf == NULL)
			THROW2(soload_ex, TRACE_INFO() << "dlsym('" << func_name << "') not found");

		tlc::get_obj<FID>(m_pfuncs).set(FID, sopf);
	}

	void release()
	{
		if(m_soh)
		{
			::dlclose(m_soh);
			m_soh = NULL;
		}
		m_enable = false;
		m_so_path = "";
	}

	template <uint32_t FID>
	typename tlop::at<fp_typelist, FID>::type get_sofunc()
	{
		return tlc::get_obj<FID>(m_pfuncs).func_ptr;
	}

	template <uint32_t FID>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type call_sofunc()
	{
		return get_sofunc<FID>()();
	}
	
	template <uint32_t FID, typename Tp1>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type call_sofunc(Tp1 p1)
	{
		return get_sofunc<FID>()(p1);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type call_sofunc(Tp1 p1, Tp2 p2)
	{
		return get_sofunc<FID>()(p1, p2);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3)
	{
		return get_sofunc<FID>()(p1, p2, p3);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3, typename Tp4>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3, typename Tp4, typename Tp5>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4, Tp5 p5)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4, p5);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3, typename Tp4, typename Tp5, typename Tp6>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4, Tp5 p5, Tp6 p6)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4, p5, p6);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3,
			typename Tp4, typename Tp5, typename Tp6, typename Tp7>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4, Tp5 p5, Tp6 p6, Tp7 p7)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4, p5, p6, p7);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3,
			typename Tp4, typename Tp5, typename Tp6, typename Tp7, typename Tp8>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4, Tp5 p5, Tp6 p6, Tp7 p7, Tp8 p8)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4, p5, p6, p7, p8);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3, typename Tp4,
			typename Tp5, typename Tp6, typename Tp7, typename Tp8, typename Tp9>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4, Tp5 p5, Tp6 p6, Tp7 p7, Tp8 p8, Tp9 p9)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4, p5, p6, p7, p8, p9);
	}
	
	template <uint32_t FID, typename Tp1, typename Tp2, typename Tp3, typename Tp4,
			typename Tp5, typename Tp6, typename Tp7, typename Tp8, typename Tp9, typename Tp10>
	typename type_traits<typename tlop::at<fp_typelist, FID>::type>::ret_type
	call_sofunc(Tp1 p1, Tp2 p2, Tp3 p3, Tp4 p4, Tp5 p5, Tp6 p6, Tp7 p7, Tp8 p8, Tp9 p9, Tp10 p10)
	{
		return get_sofunc<FID>()(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
	}
};

//////////////////
} // namespace tdl

#endif

